import request from 'request-promise'
import formstream from 'formstream'
import fs from 'fs'
import * as _ from 'lodash'
import path from 'path'
import {sign} from './util'

const base = 'https://api.weixin.qq.com/cgi-bin/'
const api = {
  accessToken: base + 'token?grant_type=client_credential',
  temporary: { // 临时素材
    upload: base + 'media/upload?',
    fetch: base + 'media/get?'
  },
  permanent: { // 永久素材
    upload: base + 'material/add_material?',
    uploadNewsPic: base + 'media/uploadimg?',
    uploadNews: base + 'material/add_news?',
    fetch: base + 'material/get_material?',
    del: base + 'material/del_material?',
    update: base + 'material/update_news?',
    count: base + 'material/get_materialcount?',
    batch: base + 'material/batchget_material?'
  },
  tag: {
    create: base + 'tags/create?',
    fetch: base + 'tags/get?',
    update: base + 'tags/update?',
    del: base + 'tags/delete?',
    fetchUsers: base + 'user/tag/get?',
    batchTag: base + 'tags/members/batchtagging?',
    batchUnTag: base + 'tags/members/batchuntagging?',
    getTagList: base + 'tags/getidlist?'
  },
  user: {
    mark: base + 'info/updateremark?',
    info: base + 'user/info?',
    userList: base + 'user/get?',
    getBlackList: base + 'tags/members/getblacklist?',
    batchBlackUsers: base + 'tags/members/batchblacklist?',
    batchUnBlackUser: base + 'tags/members/batchunblacklist?'
  },
  menu: {
    create: base + 'menu/create?',
    get: base + 'menu/get?',
    del: base + 'menu/delete?'
  },
  ticket: {
    get: base + 'ticket/getticket?'
  }
}
// 读取文件信息 fs.stat() 封装异步读取文件方法
function stat(filepath) {
  return new Promise((resolve, reject) => {
    fs.stat(filepath, (err, stat) => {
      if (err) { reject(err) }
      else {
        console.log('读取成功')
        resolve(stat)
      }
    })
  })
}

export default class Wechat {
  constructor(opts) {
    this.opts = Object.assign({}, opts)
    this.appID = opts.appID
    this.appSecret = opts.appSecret
    this.getAccessToken = opts.getAccessToken
    this.saveAccessToken = opts.saveAccessToken

    this.getTicket = opts.getTicket
    this.saveTicket = opts.saveTicket
    this.fetchAccessToken()
  }

  async request(options) {
    options = Object.assign({}, options, { json: true })

    try {
      const response = await request(options)
      console.log(response)
      return response
    } catch (error) {
      console.log(error)
    }

  }

  async fetchAccessToken() {
    let data = await this.getAccessToken()

    if (!this.isValidToken(data, 'access_token')) {
      data = await this.updateAccessToken()
    }

    await this.saveAccessToken(data)

    return data

  }
  async fetchTicket() {
    let data = await this.getTicket()

    if (!this.isValidToken(data, 'ticket')) {
      const accessToken = await this.fetchAccessToken()
      data = await this.updateTicket(accessToken.access_token)
    }
    console.log(data + '11111111111111111111')
    await this.saveTicket(data)

    return data

  }

  async updateAccessToken() {
    const url = api.accessToken + '&appid=' + this.appID + '&secret=' + this.appSecret
    console.log(url)
    const data = await this.request({ url: url })
    console.log(data)
    const now = (new Date().getTime())
    const expiresIn = now + (data.expires_in - 20) * 1000

    data.expires_in = expiresIn

    return data
  }
  async updateTicket(token) {
    const url = api.ticket.get + 'access_token=' + token + '&type=jsapi'
    const data = await this.request({ url: url })
    const now = (new Date().getTime())
    const expiresIn = now + (data.expires_in - 20) * 1000

    data.expires_in = expiresIn

    return data
  }

  isValidToken(data, name) {
    if (!data || !data[name] || !data.expires_in) {
      return false
    }

    const expiresIn = data.expires_in
    const now = (new Date().getTime())

    if (now < expiresIn) {
      return true
    } else {
      return false
    }
  }
  // 上传动作
  async handle(operation, ...args) {
    // 获取 token
    const tokenData = await this.fetchAccessToken()
    // 将所有 ...args 传进去
    const options = await this[operation](tokenData.access_token, ...args)
    // request({}) 发起请求 上传素材
    const data = await this.request(options)
    // console.log(data)
    return data
  }

  // 上传素材的功能           素材类型   原料      什么素材
  uploadMaterial(token, type, material, permanent) {
    let form = {}
    let url = api.temporary.upload

    if (permanent) {
      url = api.permanent.upload

      _.extend(form, permanent)
    }

    if (type === 'pic') {
      url = api.permanent.uploadNewsPic
    }

    if (type === 'news') {
      url = api.permanent.uploadNews
      form = material
    } else {
      form.media = fs.createReadStream(material)
    }

    let uploadUrl = url + 'access_token=' + token

    if (!permanent) {
      uploadUrl += '&type=' + type
    } else {
      if (type !== 'news') {
        form.access_token = token
      }
    }

    const options = {
      method: 'POST',
      url: uploadUrl,
      json: true
    }

    if (type === 'news') {
      options.body = form
    } else {
      options.formData = form
    }

    return options
  }

  fetchMaterial(token, type, mediaId, permanent) {
    let form = {}

    let fetchUrl = permanent ? api.permanent.fetch : api.temporary.fetch

    let url = fetchUrl + 'access_token=' + token
    let options = { method: 'POST', url: url }
    if (permanent) {
      form.media_id = mediaId
      form.access_token = token
      options.body = form
    } else {
      url += '&media_id=' + mediaId
    }
    return options
  }

  deleteMaterial(token, mediaId) {
    let url = api.permanent.del + 'access_token=' + token
    let options = { method: 'POST', url: url, body: { media_id: mediaId } }

    return options
  }
  // 只支持修改永久 news
  updataMaterial(token, mediaId, news) {
    let form = {
      media_id: mediaId,
      index: 0,
    }
    form = Object.assign(form, news)
    console.log(form)

    let url = api.permanent.update + 'access_token=' + token

    const options = { method: 'POST', url: url, body: form }

    return options
  }
  fetchCount(token) {
    let url = api.permanent.count + 'access_token=' + token
    const options = { url: url }
    return options
  }
  fetchBatch(token, options) {
    let url = api.permanent.batch + 'access_token=' + token
    options.type = options.type || 'image'
    options.offset = options.offset || 0
    options.count = options.count || 10

    return { method: 'POST', url: url, body: options }
  }
  createTag(token, name) {
    let url = api.tag.create + 'access_token=' + token
    return { method: 'POST', url: url, body: { tag: { name: name } } }
  }
  fetchTag(token) {
    let url = api.tag.fetch + 'access_token=' + token
    return { url: url }
  }
  up_delTag(token, tag, name) {
    const form = {}
    let url = api.tag.update
    if (name) {
      Object.assign(form, {
        tag: {
          id: tagId,
          name: name
        }
      })
    } else {
      Object.assign(form, { tag: { id: tagId } })
      url = api.tag.del
    }
    url = url + 'access_token=' + token
    return { method: 'POST', url: url, body: form }
  }
  fetchUsers(token, tagId, openId) {
    let url = api.tag.fetchUsers + 'access_token=' + token
    return { method: 'POST', url: url, body: { tagid: tagId, next_openid: openId || '' } }
  }
  batchTag(token, openIdList, tagId, unTag) {
    let url = unTag ? api.tag.batchUnTag : api.tag.batchTag + 'access_token=' + token
    return { method: 'POST', url: url, body: { openid_list: openIdList, tagid: tagId } }
  }
  fetchUserTag(token, openId) {
    let url = api.tag.fetchUserTag + 'access_token=' + token
    return { method: 'POST', url: url, body: { openid: openId } }
  }

  markUser(token, openId, remark) {
    let url = api.user.mark + 'access_token=' + token
    return { method: 'POST', url: url, body: { openid: openId, remark: remark } }
  }

  getUserInfo(token, openId, lang) {
    let url = `${api.user.info}access_token=${token}&openid=${openId}&lang=${lang || 'zh_CN'}`
    return { url: url }
  }
  getUserList(token, next_openId) {
    let url = `${api.user.userList}access_token=${token}&next_openid=${next_openId || ''}`
    return { url: url }
  }
  getBlackList(token, begin_openId) {
    let url = api.user.getBlackList + 'access_token=' + token
    return { method: 'POST', url: url, body: { begin_openid: begin_openId || '' } }
  }

  handleBlackUsers(token, openId_list, unBlack) {
    let url = unBlack ? api.user.batchUnBlackUser : api.user.batchBlackUsers + 'access_token=' + token
    return { method: 'POST', url: url, body: { openid_list: openId_list } }
  }

  createMenu(token, menu) {
    let url = api.menu.create + 'access_token=' + token

    return { method: 'POST', url: url, body: menu }
  }
  getMenu(token) {
    let url = api.menu.get + 'access_token=' + token
    return { url: url }
  }
  delMenu(token) {
    let url = api.menu.del + 'access_token=' + token
    return { url: url }
  }

  sign(ticket, url) {
    return sign(ticket, url)
  }

}